﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace CarCricketMatchApp
{

    /// <summary>
    /// define a delegate , in order to tell the complete event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void MatchCompleteEventHandler(object sender, MatchCompletEventArgs e);
    
    /// <summary>
    /// define a delegate, in orede to send PlayNextBall Message;
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void PlayNextBallEventHandler(object sender, PlayNextBallEventArgs e);

    /// <summary>
    /// define a delegate , in order to send facing change event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void FacingChangeEventHandler(object sender,MatchCompletEventArgs e);

    /// <summary>
    /// define a delegate, in order to send the runner change event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void RunnerChangeEventHandler(object sender,MatchCompletEventArgs e);


    /// <summary>
    /// defin a delegate, for max vechile account
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void MaxVechileAccount(object sender, MatchCompletEventArgs e);
    /// <summary>
    /// Match class 
    /// </summary>
    public class Match
    {

        #region private variables

        private Team team1;
        private Team team2;

        private const int MAXBALLACCOUNT = 500;
        private const int TEAMACCOUNT = 11;

        private Player facingPlayer;
        private Player runnerPlayer;

        private Random random;

        private int currentBallAccount;
        #endregion

        #region properties

        /// <summary>
        /// get and set team1 
        /// </summary>
        public Team Team1
        {
            get { return team1; }
            set { team1 = value; }
        }
        /// <summary>
        /// get and set team2
        /// </summary>
        public Team Team2
        {
            get { return team2; }
            set { team2 = value; }
        }
        #endregion

        #region evnets

        /// <summary>
        /// define a event, in winform we should add the event handler
        /// </summary>
        public event MatchCompleteEventHandler MatchCompleteEvent;

        /// <summary>
        /// define a event, in winform we should add the event handler
        /// </summary>
        public event PlayNextBallEventHandler PlayNextBallEvent;

        public event FacingChangeEventHandler FacingChangeEvent;

        public event RunnerChangeEventHandler RunnerChangeEvent;

        public event MaxVechileAccount MaxVechilEvent;

        #endregion


        public Match()
        {
            random = new Random();
            currentBallAccount = 0;
        }

        public Match(Team team1,Team team2):this()
        {
            this.team1 = team1;
            this.team2 = team2;
        }

        #region  public method
        public void Play()
        {
            if (team1 == null || team2 == null)
                throw new Exception("Team1 and Team2 are not ready!");
            if (team1.Players.Count != TEAMACCOUNT)
                throw new Exception("Team " + team1.Name + "player count is less than " + TEAMACCOUNT.ToString());
            if(team2.Players.Count != TEAMACCOUNT)
                throw new Exception("Team  " + team2.Name + " player count is less than " + TEAMACCOUNT.ToString());

            // check the order
            team1.checkThePlayerOrder();
            team2.checkThePlayerOrder();
            //
            DBHelper dbHelper = new DBHelper();
            // save the player to the db
          //  dbHelper.deleteAllPlayer();
             
            foreach (Player p1 in team1.Players)
            {
                try
                {
                    // delete first
                    // if exist the same player, do not insert
                    Player dbPlayer = dbHelper.getPlayer(p1.PlayerName);
                    if (dbPlayer == null)
                    {
                        // first insert ,inns set to zero
                        Player temp = new Player(p1.PlayerName);
                        temp.Inningscount = 0;
                        temp.PlayerName = p1.PlayerName;
                        temp.OwnTeam = p1.OwnTeam;
                        dbHelper.insertPlayer(temp);
                    }else
                    {
                        //int newInnsg = dbPlayer.Inningscount + p1.Inningscount;
                        //// p2.Inningscount = newInnsg;

                        //// p2.LastScore = dbPlayer.LastScore;
                        ////  p2.OutCount = dbPlayer.OutCount;
                        //Player upPlayer = new Player(p1.PlayerName);
                        //upPlayer.Inningscount = newInnsg;
                        //upPlayer.LastScore = dbPlayer.LastScore;
                        //upPlayer.OutCount = dbPlayer.OutCount;
                        //dbHelper.updatePlayer(upPlayer);

                    }
                   
                   
                }
                catch (Exception exp)
                {
                    Debug.Print("Match.cs line 142 exception :" + exp.ToString());
                }
            }

             foreach (Player p2 in team2.Players)
            {
                try
                {
                    // if exist the same player, do not insert
                    Player dbPlayer = dbHelper.getPlayer(p2.PlayerName);
                    if (dbPlayer == null)
                    {
                        // first insert ,inns set to zero
                        Player temp = new Player(p2.PlayerName);
                        temp.Inningscount = 0;
                        temp.PlayerName = p2.PlayerName;
                        temp.OwnTeam = p2.OwnTeam;
                        dbHelper.insertPlayer(temp);
                    }
                    else
                    {
                      //  // update the inns of player
                      //  //int oldInns = 0;
                      //  int newInnsg = dbPlayer.Inningscount + p2.Inningscount;
                      // // p2.Inningscount = newInnsg;

                      // // p2.LastScore = dbPlayer.LastScore;
                      ////  p2.OutCount = dbPlayer.OutCount;
                      //  Player upPlayer = new Player(p2.PlayerName);
                      //  upPlayer.Inningscount = newInnsg;
                      //  upPlayer.LastScore = dbPlayer.LastScore;
                      //  upPlayer.OutCount = dbPlayer.OutCount;
                      //  dbHelper.updatePlayer(upPlayer);

                        //
                    //    p2.LastScore = 0;
                       // p2.OutCount = 0;
                      //  p2.Inningscount = oldInns;
                    }
                }
                catch (Exception exp)
                {
                    Debug.Print("Match.cs line 155 exception :" + exp.ToString());
                }
            }
            //begin to play
            topLevelAlgorithm();
        }
        #endregion

        #region on event
        protected virtual void onMatchComplete(MatchCompletEventArgs e)
        {
            //when complete 
            //update the database
            DBHelper dbHelper = new DBHelper();
            foreach (Player p1 in team1.Players)
            {
                try
                {
                    // get from db,
                   
                    Player dbPlayer = dbHelper.getPlayer(p1.PlayerName);
                    if (dbPlayer != null)
                    {
                        

                        // plus , out account
                        int newout = dbPlayer.OutCount + p1.OutCount;
                        p1.OutCount = newout;

                        //plus, inns
                        int newins = dbPlayer.Inningscount + p1.Inningscount ;
                        p1.Inningscount = newins;

                        dbHelper.updatePlayer(p1);
                    }else
                    {

                        dbHelper.insertPlayer(p1);
                    }
                }
                catch (Exception exp)
                {
                    Debug.Print("Match.cs line 142 exception :" + exp.ToString());
                }
            }

            foreach (Player p2 in team2.Players)
            {
                try
                {
                    Player dbPlayer = dbHelper.getPlayer(p2.PlayerName);
                    if (dbPlayer != null)
                    {
                       
                        //plus account of out
                        int newout = dbPlayer.OutCount + p2.OutCount;
                        p2.OutCount = newout;

                        //plus, inns
                        int newins = dbPlayer.Inningscount + p2.Inningscount ;
                        p2.Inningscount = newins;
                        dbHelper.updatePlayer(p2);
                    }
                    else
                    {

                        dbHelper.insertPlayer(p2);
                    }
                }
                catch (Exception exp)
                {
                    Debug.Print("Match.cs line 155 exception :" + exp.ToString());
                }
            }

            if (MatchCompleteEvent != null)
            {
                MatchCompleteEvent(this, e);
            }
        }

        protected virtual void OnPlayNextBall(PlayNextBallEventArgs e)
        {
            if (PlayNextBallEvent != null)
            {
                PlayNextBallEvent(this, e);
            }
        }

        protected virtual void onFacingChange(MatchCompletEventArgs e)
        {
            if (FacingChangeEvent != null)
            {
                FacingChangeEvent(this, e);
            }
        }

        protected virtual void onRunnerChange(MatchCompletEventArgs e)
        {
            if (RunnerChangeEvent != null)
            {
                RunnerChangeEvent(this, e);
            }
        }

        protected virtual void onMaxVechilEvent(MatchCompletEventArgs e)
        {
            if(MaxVechilEvent != null)
            {
                MaxVechilEvent(this, e);
            }
        }
        #endregion

    
        //Observed car  Hitter  Balanced  Defensive 
        //    Red small car  1 run  0 run (Dot ball)  0 run (Dot ball) 
        //    Small  car but not Red  3 runs  Dot ball  Dot ball 
        //    Green or blue family-size car  2 runs  1 run   0 run  
        //    Red or white family-size car  Out  2 runs  1 run 
        //    Family-size car of other colours  4 runs  3 runs  2 runs 
        //    Red, blue, black or Green Bus  4 runs  4 runs  4 runs 
        //    White 4WD  6 runs  4 runs  2 runs 
        //    Rust 4WD  Out  6 runs  3 runs 
        //    Yellow truck  Out  Out  Dot ball 
        //    White or Grey Truck  Out  Out  Out 
        //    Road-roller any colour  No ball – 2 runs  No ball – 1 run (extra)  No ball – 1 run (extra) 
        //    Green, blue, white big-double  4 runs  2 runs  4 runs 
        //    Unspecified combinations  Dot ball  Dot ball  Dot ball 
        private Runns getResult(Vehicle ball,PlayerStyles style)
        {
            Colours color = ball.Colour;
            Types type = ball.VehicleType;
            //first row red small car
            if (color == Colours.Red && type == Types.SmallCar)
            {
                if (style == PlayerStyles.Hitter)
                {
                    return Runns.Run1;
                }else if (style == PlayerStyles.Balanced)
                {
                    return Runns.Run0;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return Runns.Run0;
                }
            }
              //second row, small car but not red
            if(type == Types.SmallCar && color != Colours.Red)
            {
                if (style == PlayerStyles.Hitter)
                {
                    return  Runns.Runs3;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return  Runns.DotBall;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return  Runns.DotBall;
                }
            }
            // thrid row, Green or blue family-size car  2 runs  1 run   0 run  
            if (type == Types.Family_sizeCar && (color == Colours.Green || color == Colours.Blue))
            {
                if (style == PlayerStyles.Hitter)
                {
                    return Runns.Runs2;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return Runns.Run1;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return Runns.Run0;
                }
            }
            // 4 row , Red or white family-size car  Out  2 runs  1 run 
            if (type == Types.Family_sizeCar && (color == Colours.Red || color == Colours.White))
            {
                if (style == PlayerStyles.Hitter)
                {
                    return Runns.Out;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return  Runns.Runs2;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return  Runns.Run1;
                }
            }//5 , Family-size car of other colours  4 runs  3 runs  2 runs 
            if (type == Types.Family_sizeCar)
            {
                if (color != Colours.Red || color != Colours.White)
                {
                    if (style == PlayerStyles.Hitter)
                    {
                        return Runns.Runs4;
                    }
                    else if (style == PlayerStyles.Balanced)
                    {
                        return Runns.Runs3;
                    }
                    else if (style == PlayerStyles.Defensive)
                    {
                        return Runns.Runs2;
                    }
                }
           
            }
            // 6 row,Red, blue, black or Green Bus  4 runs  4 runs  4 runs 
            if (type == Types.Bus && (color == Colours.Red || color == Colours.Blue || color == Colours.Black))
            {
                if (style == PlayerStyles.Hitter)
                {
                    return Runns.Runs4;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return Runns.Runs4;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return Runns.Runs4;
                }
            }

            // 7 row,White 4WD  6 runs  4 runs  2 runs 
            if (type == Types.WD4 && color == Colours.White)
            {
                if (style == PlayerStyles.Hitter)
                {
                    return  Runns.Runs6;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return Runns.Runs4;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return  Runns.Runs2;
                }
            }
            //8 row,Rust 4WD  Out  6 runs  3 runs 
            if (type == Types.WD4 && color == Colours.Rust)
            {
                if (style == PlayerStyles.Hitter)
                {
                    return  Runns.Out;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return  Runns.Runs6;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return  Runns.Runs3;
                }
            }
            //9 row, Yellow truck  Out  Out  Dot ball 
            if (type == Types.Truck && color == Colours.Yellow)
            {
                if (style == PlayerStyles.Hitter)
                {
                    return  Runns.Out;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return Runns.Out;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return  Runns.DotBall;
                }
            }
            //10 ,White or Grey Truck  Out  Out  Out
            if (type == Types.Truck && (color == Colours.White || color == Colours.Grey))
            {
                if (style == PlayerStyles.Hitter)
                {
                    return Runns.Out;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return Runns.Out;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return Runns.Out;
                }
            }
            //11 Road-roller any colour  No ball – 2 runs  No ball – 1 run (extra)  No ball – 1 run (extra) 
            if (type == Types.Road_roller )
            {
                if (style == PlayerStyles.Hitter)
                {
                    return  Runns.NoBall2Runs;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return Runns.NoBall1Runextra;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return Runns.NoBall1Runextra;
                }
            }
            //12  Green, blue, white big-double  4 runs  2 runs  4 runs 
            if (type == Types.Big_double && (color == Colours.Green || color == Colours.Blue) )
            {
                if (style == PlayerStyles.Hitter)
                {
                    return Runns.Runs4;
                }
                else if (style == PlayerStyles.Balanced)
                {
                    return Runns.Runs2;
                }
                else if (style == PlayerStyles.Defensive)
                {
                    return  Runns.Runs4;
                }
            }
            //13,Unspecified combinations  Dot ball  Dot ball  Dot ball 
            return Runns.DotBall;
        }

        private void topLevelAlgorithm()
        {
            //
            while(true)
            {
                try
                {
                    //Both teams have completed their innings. Team with the larger score is the winner. If two teams have the same score, the match is a tie.
                    if (this.team1.Inning == Inns.CompleteIn && this.team2.Inning == Inns.CompleteIn)
                    {
                        // comple
                        // on event
                        //get the scores,
                        int team1Score = team1.getTeamScore();
                        int team2Score = team2.getTeamScore();
                        string showMesseg = "";
                        if (team1Score > team2Score)
                        {
                            showMesseg = "Win team is " + team1.Name + ", score is: " + team1Score;
                        }
                        else if (team1Score == team2Score)
                        {
                            showMesseg = "Tie";
                        }
                        else
                        {
                            showMesseg = "Win team is " + team2.Name + ", score is: " + team2Score;
                        }
                        MatchCompletEventArgs e = new MatchCompletEventArgs(showMesseg, team1Score.ToString(),team2Score.ToString());
                        onMatchComplete(e);
                        //and break loop
                        break;
                    }

                    //One team has completed its inning and the other team has scored more runs. The latter team is the winning team. The match ends when this situation is reached.

                    if (team1.Inning == Inns.CompleteIn)
                    {
                        int team1Score = team1.getTeamScore();
                        int team2Score = team2.getTeamScore();
                        if (team2Score > team1Score)
                        {
                            string showMesseg = "Win team is " + team2.Name + ", score is: " + team2Score.ToString();

                            MatchCompletEventArgs e = new MatchCompletEventArgs(showMesseg,team1Score.ToString(),team2Score.ToString());
                            onMatchComplete(e);
                            break;
                        }
                    }

                    if (team2.Inning == Inns.CompleteIn)
                    {
                        int team1Score = team1.getTeamScore();
                        int team2Score = team2.getTeamScore();
                        if (team1Score > team2Score)
                        {
                            string showMesseg = "Win team is " + team1.Name + ", score is: " + team1.getTeamScore().ToString();

                            MatchCompletEventArgs e = new MatchCompletEventArgs(showMesseg, team1Score.ToString(), team2Score.ToString());
                            onMatchComplete(e);
                            break;
                        }
                    }

                    if (facingPlayer == null && runnerPlayer == null)
                    {
                        // first time, get player from team1 and team2
                        // get the team1 
                        Player player1 = team1.getOrderPlayer();
                        Player player2 = team2.getOrderPlayer();

                        this.facingPlayer = player1;
                        // on facing player chagne event
                        onFacingChange(new MatchCompletEventArgs(facingPlayer.PlayerName));

                        this.runnerPlayer = player2;
                        // on runner player change event
                        onRunnerChange(new MatchCompletEventArgs(runnerPlayer.PlayerName));
                    }
                    else
                    {
                        //facingPlayer = null;
                        //  facingPlayer = runnerPlayer;

                        //if (runnerPlayer != null)
                        //{
                        //    // move the next 
                        //    Team team = runnerPlayer.OwnTeam;
                        //    runnerPlayer = team.getOrderPlayer();
                        //}
                        //else
                        //{
                        //    throw new Exception("Null pointer exception!");
                        //}
                        //get the runnerplayer team
                    }


                    while (true)
                    {
                        try
                        {

                            // get the randmo ball
                            int colorInt = random.Next(0, 9);
                            int typeInt = random.Next(0, 9);
                            Colours color = (Colours)Enum.Parse(typeof(Colours), colorInt.ToString());
                            Types type = (Types)Enum.Parse(typeof(Types), typeInt.ToString());
                            Vehicle ball = new Vehicle(color, type);
                            /// send the message
                            OnPlayNextBall(new PlayNextBallEventArgs(ball));
                            Debug.Print("Math.cs line 477 :" + ball.ToString());
                            this.currentBallAccount += 1;

                            if (currentBallAccount == MAXBALLACCOUNT)
                            {
                                //500 balls have been played. The winner is decided by the scores of the teams. If, however,
                                //both teams have the same score, then the winner is decided by the number of players out 
                                //for each team. The team which has fewer out players is declared the winner. If the result is 
                                //still not resolved, the match is declared a tie.
                                // comple
                                // on event
                                //get the scores,
                                onMaxVechilEvent(new MatchCompletEventArgs("Vechile count is 500"));
                                int team1Score = team1.getTeamScore();
                                int team2Score = team2.getTeamScore();
                                string showMesseg = "";
                                if (team1Score > team2Score)
                                {
                                    showMesseg = "Win team is " + team1.Name + ", score is: " + team1Score;
                                }
                                else if (team1Score == team2Score)
                                {
                                    if (team1.OutPlayers.Count < team2.OutPlayers.Count)
                                    {
                                        showMesseg = "Win team is " + team1.Name;
                                    }
                                    else if (team1.OutPlayers.Count > team2.OutPlayers.Count)
                                    {
                                        showMesseg = "Win team is " + team2.Name;
                                    }
                                    else
                                        showMesseg = "Tie";
                                }
                                else
                                {
                                    showMesseg = "Win team is " + team2.Name + ", score is: " + team2Score;
                                }
                                MatchCompletEventArgs e = new MatchCompletEventArgs(showMesseg,team1Score.ToString(),team2Score.ToString());
                                onMatchComplete(e);
                                //and break loop
                                return;
                            }
                            else
                            {
                                //if facing player is null, the end of match
                                if (facingPlayer == null)
                                {
                                    break;
                                }
                                //get the result
                                Runns result = getResult(ball, facingPlayer.Style);
                                //0 run
                                if (result == Runns.Run0)
                                {
                                    // contiue to next ball
                                    Debug.Print("Match line 525 :  result is run 0 ");
                                    continue;
                                }
                                //Dot ball
                                if (result == Runns.DotBall)
                                {
                                    // contiue to next ball
                                    Debug.Print("Match line 532 :  result is Dot Ball ");
                                    continue;
                                }
                                if (result == Runns.NoBall1Runextra)
                                {
                                    // and extra1 to the team extra
                                    Team temp = facingPlayer.OwnTeam;
                                    temp.ExtraScore = temp.ExtraScore + 1;
                                    Debug.Print("Match line 540 :  No Ball 1 run extra");
                                    // contiue to next ball
                                    continue;
                                }
                                if (result == Runns.NoBall2Runs)
                                {
                                    // and extra1 to the team extra
                                    Team temp = facingPlayer.OwnTeam;
                                    temp.ExtraScore = temp.ExtraScore + 2;
                                    Debug.Print("Match line 549 :  No Ball 2 run extra ");
                                    // contiue to next ball
                                    continue;
                                }

                                if (result == Runns.Out)
                                {
                                    // change the facing and runner
                                    this.facingPlayer.Status = States.Out;


                                    //when out, record the out count
                                    this.facingPlayer.OutCount = this.facingPlayer.OutCount + 1;

                                    //if out, add to the out players
                                    Team team = this.facingPlayer.OwnTeam;
                                    this.facingPlayer.Status = States.Out;
                                    team.addPlayerToOutTheTeam(facingPlayer);
                                    Debug.Print("$$$$$$$$$$$$$$　Match line 568 :  Out  " + facingPlayer.PlayerName);

                                    // when out, set the current runner to facingplayer
                                    this.facingPlayer = this.runnerPlayer;
                                    Debug.Print("Match line 572 :  new Facing " + facingPlayer.PlayerName);
                                    //on event
                                    onFacingChange(new MatchCompletEventArgs(this.facingPlayer.PlayerName));

                                    //and change the runner
                                    this.runnerPlayer = team.getOrderPlayer();

                                    if (runnerPlayer  != null)
                                    {
                                        Debug.Print("Match line 576 :  new runner   " + runnerPlayer.PlayerName);
                                        // on event 
                                        onRunnerChange(new MatchCompletEventArgs(runnerPlayer.PlayerName));
                                    }
                                  
                                    // and break this while.
                                    break;
                                }

                                if (result == Runns.Run1 || result == Runns.Runs3)
                                {
                                    // and the score 
                                    facingPlayer.CurrentScore = (int)result;
                                    facingPlayer.LastScore = this.facingPlayer.LastScore + (int)result;

                                    //swiap the facing and runner
                                    Player temp = null;
                                    temp = facingPlayer;
                                    facingPlayer = runnerPlayer;
                                    runnerPlayer = temp;
                                    if (facingPlayer != null && runnerPlayer != null)
                                    {
                                        Debug.Print("****************** Match line 572 :  Swip  after, facing: " + facingPlayer.PlayerName + "    " + runnerPlayer.PlayerName);
                                    }
                                    continue;
                                }

                                facingPlayer.CurrentScore = (int)result;
                                facingPlayer.LastScore = this.facingPlayer.LastScore + (int)result;
                            }
                            //thead sleep 1 second
                           // Thread.Sleep(1000);
                        }
                        catch (Exception exp)
                        {
                            Debug.Print(exp.ToString());
                        }

                    }
                    //sleep 2 seconds
                   // Thread.Sleep(2000);
                }
                catch (System.Exception ex)
                {
                    Debug.Print(ex.ToString());
                }

            }
        }

   
    }
}
